﻿namespace ECM7.Themes.Domain.Services
{
	using System;
	using System.IO;
	using System.Text;
	using ECM7.Themes.Domain.Model.Base;
	using ECM7.Themes.Domain.Model.Styles;
	using ECM7.Themes.Domain.Model.Templates;
	using Model;
	using NVelocity;
	using NVelocity.App;

	using Template = ECM7.Themes.Domain.Model.Templates.Template;

	/// <summary>
	/// Генератор тем оформления
	/// </summary>
	public class ThemeBuilder
	{
		/// <summary>
		/// Сгенерировать тему оформления
		/// </summary>
		/// <param name="targetDirectory">Папка для сохранения результата</param>
		/// <param name="config">Конфигурация</param>
		/// <param name="data">Данные, введенные пользователем</param>
		/// <param name="isPreview">Признак, что генерируется страница дял предварительного просмотра</param>
		public void Build(string targetDirectory, ThemeConfiguration config, UserData data, Boolean isPreview)
		{
			BuildTemplate(targetDirectory, config.Templates[data.TemplateGuid], data, isPreview);
			BuildCss(targetDirectory, config.Style, data);
		}

		#region private

		/// <summary>
		/// Формируем результирующий CSS-файл и записываем его на диск
		/// </summary>
		private static void BuildCss(string targetDirectory, Style style, UserData userData)
		{
			// todo: вынести зашитые в коде имена файлов в отдельный класс с константами
			string cssFilePath = Path.Combine(targetDirectory, "master.css");

			using (FileStream stream = new FileStream(cssFilePath, FileMode.OpenOrCreate))
			{
				using (TextWriter writer = new StreamWriter(stream, Encoding.UTF8))
				{
					WriteCssRecursive(style, userData, writer);
				}
			}
		}

		/// <summary>
		/// Формируем результирующий файл на основе шаблона и записываем его на диск
		/// </summary>
		private static void BuildTemplate(string targetDirectory, Template template, UserData data, Boolean isPreview)
		{
			VelocityContext context = new VelocityContext();

			PutParameters(context, template, data);
			SetPlaceHolders(context, template, isPreview);

			string targetFilePath = Path.Combine(targetDirectory, template.Name + ".html");
			NVelocity.Template fileTemplate = Velocity.GetTemplate(template.FilePath);

			using (FileStream stream = new FileStream(targetFilePath, FileMode.Create))
			{
				using (TextWriter writer = new StreamWriter(stream, Encoding.UTF8))
				{
					fileTemplate.Merge(context, writer);
				}
			}
		}

		/// <summary>
		/// Получить контекст предварительного просмотра
		/// </summary>
		private static void SetPlaceHolders(VelocityContext context, Template template, bool isPreview)
		{
			foreach (PlaceHolder placeHolder in template.Contents)
			{
				context.Put(placeHolder.Name, placeHolder.GetContent(isPreview));
			}
		}

		/// <summary>
		/// Добавить параметры шаблона в сонтент
		/// </summary>
		private static void PutParameters(VelocityContext context, Template template, UserData data)
		{
			foreach (var parameter in template.Parameters)
			{
				context.Put(parameter.Value.Name, data.Values[parameter.Key]);
			}
		}

		#endregion

		#region styles

		/// <summary>
		/// Рекурсивная запись в файл дерева стилей
		/// </summary>
		/// <param name="style">Описание стиля</param>
		/// <param name="userData">Пользовательские данные</param>
		/// <param name="writer">Объект, выполняющий запись в результирующий файл</param>
		private static void WriteCssRecursive(Style style, UserData userData, TextWriter writer)
		{
			foreach (var param in style.Parameters)
			{
				if (userData.Values.ContainsKey(param.Key))
				{
					WriteStyle(param.Value.Selector, userData.Values[param.Key], writer);
				}
			}

			if (style.ChildStyles != null)
			{
				foreach (var childStyle in style.ChildStyles)
				{
					WriteCssRecursive(childStyle, userData, writer);
				}
			}
		}

		/// <summary>
		/// Записывает в файл элемент CSS
		/// </summary>
		/// <param name="selector">Селектор</param>
		/// <param name="data">Значение</param>
		/// <param name="writer">writer</param>
		private static void WriteStyle(string selector, string data, TextWriter writer)
		{
			string content = (data ?? string.Empty).Trim();
			if (string.IsNullOrEmpty(content))
			{
				return;
			}

			writer.WriteLine(selector);
			writer.WriteLine("{");

			var lines = content.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
			foreach (string line in lines)
			{
				writer.WriteLine("\t" + line);
			}

			writer.WriteLine("}");
			writer.WriteLine();
		}

		#endregion
	}
}
